// 问题1：数据拦截只能拦截第一层
// 问题2：节点匹配会将所有的都改成同一个值

class Kvue extends EventTarget{
  constructor(opts) {
    super();
    this.$options = opts;
    this.$el = document.querySelector(this.$options.el);
    
    this.compile();
    this.observe(this.$options.data);
  }

  // 观察者
  observe(data) {
    let keys = Object.keys(data);
    keys.forEach(key => {
      this.defineReact(data, key, data[key]);
    })
  }

  // 数据拦截函数
  defineReact(data, key, value) {
    let _this = this;
    Object.defineProperty(data, key, {
      configurable: true,
      enumerable: true,
      get() {
        return value;
      },
      set(newVal) {
        // let event = new Event(key);
        let event = new CustomEvent(key, {
          detail: newVal
        })
        _this.dispatchEvent(event);
        value = newVal;
      }
    })
  }

  // 编译器
  compile() {
    this.compileNode(this.$el);
  }

  // 编译节点(将opts中data的数据展示在页面上)
  compileNode(el) {
    let childNodes = el.childNodes;
    childNodes.forEach(node => {
      if (node.nodeType === 1) {
        // 标签
        let attrs = node.attributes; // 伪数组，[...arr]变成数组
        [...attrs].forEach(attr => {
          let attrName = attr.name;
          let attrValue = attr.value;
          if (attrName.indexOf('v-') === 0) {
            attrName = attrName.substr(2);
            if (attrName === 'html') {
              node.innerHTML = this.$options.data[attrValue];
            } else if (attrName === 'model') {
              node.value = this.$options.data[attrValue];
              node.addEventListener('input', e => {
                this.$options.data[attrValue] = e.target.value;
              })
            }
          }
        })
        
        if (node.childNodes.length > 0) {
          this.compileNode(node);
        }
      } else if (node.nodeType === 3) {
        // 文本节点
        // ()分组
        let reg = /\{\{\s*(\S+)\s*\}\}/g;
        let textContent = node.textContent;
        if (reg.test(textContent)) {
          let $1 = RegExp.$1;
          // node.textContent = this.$options.data[$1]; // 不好，会把所有的节点全部赋值
          node.textContent = node.textContent.replace(reg, this.$options.data[$1]);
          this.addEventListener($1, ({ detail }) => {
            let oldVal = this.$options.data[$1];
            let reg = new RegExp(oldVal);
            node.textContent = node.textContent.replace(reg, detail);
          })
        }
      }
    })
  }
}